
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sstream>
#include <glib.h>
#include "AntDefine.h"
#include "define.h"
#include "Debug.h"
#include "Helper.h"
 
using namespace std;
Helper * Helper::_instance = NULL;

Helper::Helper() 
{
	m_sConfFile = "";
	m_sIPAcl = "";
	m_iPort = 0;
	m_iwPort = 0;
	m_iThreads = 10;
        m_iDefaultTimer = 0;
        m_iCounts = 0;
        m_pEvents = NULL;
}

Helper::~Helper()
{
    if(m_pEvents != NULL)
       delete m_pEvents;
}

Helper * Helper::Instance(const char *confFile)
{
	if (NULL == _instance) 
	{
		_instance = new Helper();
		if ( !_instance->Startup(confFile) ) {
			delete _instance; 
			_instance = NULL;
		}
	}
	return _instance;
}

void Helper::Release(bool bForce)
{
	if (_instance != NULL) 
	{
		if(bForce)
		{
			delete _instance; 
			_instance = NULL;
		}
	}
}

bool  Helper::Startup(const char *confFile)
{
        m_iStartTime = time(NULL);
	if(confFile != NULL)
		m_sConfFile  = confFile;
	if(m_sConfFile.length() < 1)
	{
		ERRO<<": no helper.conf be given!"<<endl;
		return false;
	}
	struct stat st;
	if (lstat(confFile, &st) != 0)
	{
		ERRO<<":"<<confFile<<" is not exist!"<<endl;
		return false;
	}

	GKeyFile *kv = g_key_file_new();
	GError *err = NULL;
	if(TRUE != g_key_file_load_from_file(kv,confFile, G_KEY_FILE_NONE , &err))
	{
		if (err != NULL)
		{
			ERRO << "Unable to read file:" << err->message << endl;
			g_error_free (err);
		}
		g_key_file_free(kv);
		return false;
	}

	char *pval =  g_key_file_get_value(kv,"global", "ip_acl_config",  NULL);
	if(pval == NULL)
	{ 
		m_sIPAcl = "";
		ERRO <<": no ip_acl_config setting "<< endl;
	} 
	else
	{

		m_sIPAcl  = pval;
		struct stat st;
		if (lstat((char *)pval , &st) != 0)
		{
			ERRO<< " file not exist:"<< pval <<endl ;
			m_sIPAcl = ""; 
		}
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "port",  NULL);
	if(pval == NULL)
		m_iPort = 4080;
	else
	{
		m_iPort = atoi((char *) pval);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "wport",  NULL);
	if(pval == NULL)
		m_iwPort = 9443;
	else
	{
		m_iwPort = atoi((char *) pval);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global", "threads",  NULL);
	if(pval == NULL)
		m_iThreads = 10;
	else
	{
		m_iThreads = atoi((char *) pval);
		g_free(pval);
	}

	int iGCSize = 100000;
	pval =  g_key_file_get_value(kv,"global" ,"gcSize",  NULL);
	if(pval != NULL)
	{
		iGCSize = atoi((char *) pval);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global" , "defaultTimer",  NULL);
	if(pval != NULL)
	{
		DEBUG2("system will use timer event to process task list!, timer = ", pval);
		m_iDefaultTimer = atoi((char *) pval);
		g_free(pval);
	}
        else
        {       DEBUG1("No defaultTimer! system no timer to be set ");    }

	float fGCfactor = 0.6;
	pval =  g_key_file_get_value(kv,"global" , "gcFactor",  NULL);
	if(pval != NULL)
	{
		fGCfactor = atof((char *) pval);
		g_free(pval);
		if(fGCfactor <= 0 || fGCfactor >= 1 )
			fGCfactor = 0.6;
	}

	int kickout = 84000;
	pval =  g_key_file_get_value(kv,"global" , "kickout",  NULL);
	if(pval != NULL)
	{
		kickout = atoi((char *) pval);
		g_free(pval);
		if(kickout < 1  )
			kickout =  1;
	}

	string scName;
	pval =  g_key_file_get_value(kv, "global","consumerName", NULL);
	if(pval != NULL)
	{
		scName = pval;
		g_free(pval);
	}

	string scLibrary;
	pval =  g_key_file_get_value(kv,"global" , "consumerLib",  NULL);
	if(pval != NULL)
	{
		scLibrary = pval;
		struct stat st;
		if (lstat((char *)pval , &st) != 0)
		{
			ERRO<< " file not exist:"<< pval <<endl ;
			scLibrary = ""; 
		}
		g_free(pval);
	}
	string scConfFile;
	pval =  g_key_file_get_value(kv,"global" ,"consumerConf", NULL);
	if(pval != NULL)
	{
		scConfFile = pval;
		struct stat st;
		if (lstat((char *)pval , &st) != 0)
		{
			ERRO<< " file not exist:"<< pval <<endl ;
			scConfFile  = ""; 
		}
		g_free(pval);

	}
	if( m_pEvents == NULL)
	{
		m_pEvents  = new Events(scName.c_str(), scLibrary.c_str(), scConfFile.c_str(), iGCSize, fGCfactor, kickout);
		if(!m_pEvents->Initialize())
		{
			ERRO<<" Events can't be Initialize"<<endl;
                        delete m_pEvents;
                        m_pEvents = NULL;
                        g_key_file_free(kv);
			return false;
		}
	}
	else
	{
                g_key_file_free(kv);
		ERRO<<"Events is not NULL ??? !!!"<<endl;
		return false;
	}

	pval =  g_key_file_get_value(kv,"global" ,"returnType", NULL);
	if(pval != NULL)
	{
                int it = atoi(pval);
                DEBUG2("set ReturnType = ", it);
                m_pEvents->setReturnType(it);
		g_free(pval);
	}
	pval =  g_key_file_get_value(kv,"global" ,"updateMax", NULL);
	if(pval != NULL)
	{
                int imax = atoi(pval);
                DEBUG2("set updateMax = ", imax);
                m_pEvents->setUpdateMax(imax);
		g_free(pval);
	}

	pval =  g_key_file_get_value(kv,"global" ,"returnMaxCounts", NULL);
	if(pval != NULL)
	{
		int imax = atoi(pval);
		DEBUG2("set returnMaxCounts = ", imax);
		m_pEvents->setReturnMaxCounts(imax);
		g_free(pval);
	}

        g_key_file_free(kv);
	return true;
}

int Helper :: get_mem_return_info(char **wbuf, int retcode,unsigned &wsize, unsigned &wbytes)
{

	int uint_len = sizeof(int);
	wbytes = 2*uint_len;
	DEBUG4("wsize=", wsize, "; wbytes=", wbytes );
	if(wsize < wbytes)
	{
DEBUG1("realloc(*wbuf, wbytes+1)");
		char *new_buf = (char *)g_realloc(*wbuf, wbytes +1);
		if(new_buf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wsize = 0;
			return  -1;
		}
                *wbuf = new_buf;
		wsize = wbytes + 1;
	}

        unsigned usedlen = 0;
DEBUG4("wbuf =", *wbuf,"; bcopy wbytes = ", wbytes);
        bcopy(&wbytes, *wbuf + usedlen, uint_len);            //str_length
        usedlen += uint_len ;
DEBUG2("bcopy retcode = ", retcode);
        bcopy(&retcode, *wbuf + usedlen, uint_len );   // retcode
        
        return 0;
}
int Helper :: Process(char *data, unsigned dlen,char **wbuf, unsigned &wsize, unsigned &wbytes, const char *ip)
{ 
        unsigned int uint_len = sizeof(unsigned);
        int iret = BRUNCE_ERR_SYSTEM;
        if( dlen >= 3)   // imethod + ihash + ?
        {
                unsigned imethod;
                char *pChar = data;
                bcopy(pChar, &imethod, uint_len);
                char cTitle = pChar[0];
                int iMethod = (int)pChar[1];
                DEBUG6("c->rbytes = ", dlen," title=",(char)cTitle, "; method = ", iMethod );
                if(cTitle == 'y' && iMethod>0 && iMethod <= 10)
                    iret = do_Method(iMethod, pChar+2, dlen - 2, wbuf, wsize, wbytes);
       }
        else
		return ANT_CONN_ERR_CLOSE;

	if(iret < 0 && iret != BRUNCE_ERR_NO_DATA)
		return ANT_CONN_ERR_CLOSE;

	return iret;
}


int Helper :: do_Method(int method, char *data, unsigned dlen,char **wbuf, unsigned &wsize, unsigned &wbytes)
{
	wbytes = 0;
	int iret = -1;
	DEBUG8("wbuf =", *wbuf, "; wsize=", wsize,"; wbytes=", wbytes,"; method=", method );
        m_iCounts++;
	switch(method)
	{ 
		case BRUNCE_GET_GRAPH:
			iret = Get_Users_Graph(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
                        }
			break;
		case BRUNCE_GET_FRIENDS:
			iret = Get_User_Friends(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
                        }
			break;
		case BRUNCE_GET_NUM_FRIENDS:
			iret = Get_User_Num_Friends(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
                        }
			break;
		case BRUNCE_GET_TYPE_FRIENDS:
			iret = Get_User_Type_Friends(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
                        }
			break;
		case BRUNCE_FRIENDS_FRIENDS:
			iret = Get_Friends_Friends(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
			}
			break;
		case BRUNCE_GET_UVALUE:
			iret = Get_Users_Value(data, dlen, wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
				DEBUG2("error:  iret = ", iret);
				get_mem_return_info(wbuf, iret, wsize, wbytes); 
			}
			break;

		case BRUNCE_RESET_USER:
			iret = ReSet_User(data, dlen);
			break;
		case BRUNCE_GET_STATUS:
			iret = Get_Status(wbuf, wsize, wbytes);
			if(iret != BRUNCE_SUCCESS || wbytes < 1) 
			{
                                DEBUG2("error:  iret = ", iret);
                        	get_mem_return_info(wbuf, iret, wsize, wbytes); 
                        }
			break;
		default:
			DEBUG2("not defined method = ", method);
			iret = BRUNCE_ERR_ARGV;
                       	get_mem_return_info(wbuf, iret, wsize, wbytes); 
			break;
	}
 	DEBUG6("wbuf =", *wbuf, "; wsize=", wsize,"; wbytes=", wbytes );
	return iret;
}

int Helper :: Timer(long lt)
{
	if(m_pEvents != NULL)
		m_pEvents->Process_Worklist(lt);
	return 0;
}

int Helper:: Get_Users_Graph(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval + ulen + uval
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len*2 + 2)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }

	bcopy(data, &ulen, uint_len);
	used = uint_len;
	if(dlen < (used + ulen+ uint_len + 1))
	{
		DEBUG4("length error: dlen < used+9 : dlen=",dlen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
        char *user1 = g_strndup(data+uint_len, ulen);
        used += ulen;
	bcopy(data + used , &ulen,  uint_len);
	used += uint_len;
	/*   need to g_free  */
	if(dlen < (used + ulen))
	{
                g_free(user1);
		DEBUG4("length error: dlen < used+4+ulen: ulen=", ulen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
	char *user2 = g_strndup((char *)data + used, ulen);
	if(m_pEvents != NULL && user1 != NULL && user2 != NULL)
		iret = m_pEvents->Get_Users_Graph(user1, user2, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
        g_free(user1); 
        g_free(user2); 
	return iret;
}

int Helper:: Get_User_Friends(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval 
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }
        
	bcopy(data, &ulen, uint_len);
	used = uint_len;

	if(dlen < (used + ulen))
	{
		DEBUG4("length error: dlen < used+9 : dlen=",dlen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
        char *user = g_strndup(data+uint_len, ulen);
	if(m_pEvents != NULL && user != NULL)
		iret = m_pEvents->Get_User_Friends(user, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
        g_free(user); 
	return iret;
}

int Helper:: Get_User_Type_Friends(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval + type + start + num
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len*4 + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }
        
	bcopy(data, &ulen, uint_len);
	used = uint_len;

	if(dlen < (used + ulen + 3*uint_len ))
	{
		DEBUG4("length error: dlen < used+9 : dlen=",dlen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
        char *user = g_strndup(data+uint_len, ulen);
        used += ulen;
        unsigned int type = 0;
	bcopy(data+used , &type, uint_len);
        unsigned start = 0;
        unsigned num = 100; 
        used += uint_len ;
	bcopy(data+used , &start, uint_len);
        used += uint_len;
	bcopy(data+used , &num, uint_len);

	if(m_pEvents != NULL && user != NULL)
		iret = m_pEvents->Get_User_Friends(user, type, start, num, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
        g_free(user); 
	return iret;
}


int Helper:: Get_User_Num_Friends(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval + start + num
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len*3 + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }
        
	bcopy(data, &ulen, uint_len);
	used = uint_len;

	if(dlen < (used + ulen + 2*uint_len ))
	{
		DEBUG4("length error: dlen < used+9 : dlen=",dlen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
        char *user = g_strndup(data+uint_len, ulen);
        used += ulen;
        unsigned int start = 0;
        unsigned int num = 0;
	bcopy(data+used , &start, uint_len);
	used += uint_len;
	bcopy(data+used , &num , uint_len);
      	if(num <=0)
	{
		DEBUG3("num =", num,", it's error!");  
		return BRUNCE_ERR_ARGV;
	}

	if(m_pEvents != NULL && user != NULL)
		iret = m_pEvents->Get_User_Friends(user,start, num, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
        g_free(user); 
	return iret;
}


int Helper:: Get_Friends_Friends(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval + ulen + uval
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }
/////////////////////////////////////////////////////////////////////////
//
//  stop here!!
        return BRUNCE_ERR_SYSTEM;
        
	bcopy(data, &ulen, uint_len);
	used = uint_len;
	if(dlen < (used + ulen))
	{
		DEBUG4("length error: dlen < used+9 : dlen=",dlen,"; used = ", used);  
		return BRUNCE_ERR_ARGV;
	}
        char *user = g_strndup(data+uint_len, ulen);
	if(m_pEvents != NULL && user != NULL)
		iret = m_pEvents->Get_Friends_Friends(user, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
        g_free(user); 
	return iret;
}

int Helper:: Get_Users_Value(char *data, unsigned dlen, char **wbuf, unsigned &wsize, unsigned &wbytes)
{ //counts + ulen +user + ... ...
        unsigned uint_len = sizeof(unsigned);
        int iret = BRUNCE_ERR_ARGV;
        if(dlen < uint_len*2 + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }
         
	if(m_pEvents != NULL )
		iret = m_pEvents->Get_Users_Value(data, dlen, wbuf, wsize, wbytes);
	else
		ERRO<<"Event object is NULL "<<endl;
	return iret;
}


int Helper:: ReSet_User(char *data, unsigned dlen)
{ // ulen +uval + ulen + uval
        unsigned ulen = 0;
        unsigned uint_len = sizeof(unsigned);
        unsigned used = 0;
        int iret = -1;
        if(dlen < uint_len + 1)
        {
              DEBUG2("data length is too small, dlen=", dlen);
              return  BRUNCE_ERR_ARGV;
        }

	int icount = 0;
	bcopy(data, &icount, uint_len);
        DEBUG2("user counts = ", icount);
	used = uint_len;
	for(int i=0; i< icount; i++)
	{
		if(dlen < (used + uint_len + 1))
		{
			DEBUG4("length error: dlen=",dlen,"; used = ", used);  
                        iret = BRUNCE_ERR_ARGV;
			break;
		}
		bcopy(data + used , &ulen,  uint_len);
		used += uint_len;
		if(dlen < (used + ulen))
		{
                        iret = BRUNCE_ERR_ARGV;
			DEBUG4("length error: ulen=", ulen,"; used = ", used);  
			break;
		}
		char *user = g_strndup((char *)data + used, ulen);
                DEBUG2("reset user=", user);
		if(m_pEvents != NULL && user != NULL)
			m_pEvents->Reload_User(user); 
                else
                    ERRO<<"Event object is NULL "<<endl; 
		g_free(user);
                used += ulen;
	}
	return iret;
}

int Helper:: Get_Status(char **wbuf, unsigned &wsize, unsigned &wbytes)
{ // ulen +uval + ulen + uval
 // return :  length + retcode + usercount + kickout + maxcount + time + request
	if(m_pEvents == NULL )
	return BRUNCE_ERR_SYSTEM;

	if(wsize < 1024)
	{
		DEBUG1("realloc(*wbuf, wbytes+1)");
		char * new_wbuf = (char *)g_realloc(*wbuf, 1024);
		if(new_wbuf == NULL){
			ERRO<<"can't malloc for write buffer !"<<endl;
			wbytes = 0;
			return BRUNCE_ERR_SYSTEM;
		}
		wsize = 1024;
                *wbuf = new_wbuf;
	}

	m_pEvents->Get_Status(wbuf, wsize, wbytes); 
	char hostname[128];
        gethostname(hostname, sizeof(hostname)) ;
        int hlen = strlen(hostname);
        int ulen = sizeof(unsigned);
        if(wsize < wbytes + 2*ulen + hlen )
        {
		char *new_wbuf = (char *)realloc(*wbuf,  wsize + 2*ulen + hlen);
                if(new_wbuf == NULL)
                        return BRUNCE_ERR_SYSTEM;      
                *wbuf = new_wbuf;
		wsize = wsize + 2*ulen + hlen;
	}      

        bcopy(&m_iCounts, (*wbuf)+wbytes, ulen);
	bcopy(&m_iStartTime, (*wbuf)+wbytes + ulen, ulen);
	wbytes += 2*ulen;      
	bcopy(&hlen, (*wbuf)+wbytes, ulen);
        wbytes += ulen;	
	bcopy(hostname, (*wbuf)+wbytes, hlen );
	wbytes += hlen; 
	int vrlen = strlen(COBWEB_VERSION);
	bcopy(&vrlen, (*wbuf)+wbytes, ulen);

        wbytes += ulen;	
  	bcopy(COBWEB_VERSION, (*wbuf)+wbytes,  vrlen );
        wbytes += vrlen;
	bcopy(&wbytes, (*wbuf), ulen);

        return BRUNCE_SUCCESS;	
}

bool Helper:: Clear()
{ // ulen +uval + ulen + uval
	if(m_pEvents != NULL )
		return	m_pEvents->Clear();
	else
		return false;
}


